package com.sy.simpleegg.util

import android.content.Context
import android.text.TextUtils
import android.util.Log
import com.sy.simpleegg.ext.logFile
import com.sy.logger.Logger
import org.json.JSONObject
import java.io.BufferedReader
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.io.RandomAccessFile
import java.text.DecimalFormat
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream


object FileUtil {
    /** 删除文件，可以是文件或文件夹
     * @param delFile 要删除的文件夹或文件名
     * @return 删除成功返回true，否则返回false
     */
    fun delete(delFile: String): Boolean {
        val file = File(delFile)
        return if (!file.exists()) {
            //            Toast.makeText(HnUiUtils.getContext(), "删除文件失败:" + delFile + "不存在！", Toast.LENGTH_SHORT).show();
            Logger.d("删除文件失败:" + delFile + "不存在！")
            false
        } else {
            if (file.isFile) deleteSingleFile(delFile) else deleteDirectory(delFile)
        }
    }

    /** 删除单个文件
     * @param filePathName 要删除的文件的文件名
     * @return 单个文件删除成功返回true，否则返回false
     */
    fun deleteSingleFile(`filePath$Name`: String): Boolean {
        val file = File(`filePath$Name`)
        // 如果文件路径所对应的文件存在，并且是一个文件，则直接删除
        return if (file.exists() && file.isFile) {
            if (file.delete()) {
                Logger.d(
                    "--Method--",
                    "Copy_Delete.deleteSingleFile: 删除单个文件" + `filePath$Name` + "成功！"
                )
                true
            } else {
                Logger.d("删除单个文件" + `filePath$Name` + "失败！")
                false
            }
        } else {
            Logger.d("删除单个文件失败：" + `filePath$Name` + "不存在！")
            false
        }
    }

    /** 删除目录及目录下的文件
     * @param filePath 要删除的目录的文件路径
     * @return 目录删除成功返回true，否则返回false
     */
    fun deleteDirectory(filePath: String): Boolean {
        // 如果dir不以文件分隔符结尾，自动添加文件分隔符
        var filePath = filePath
        if (!filePath.endsWith(File.separator)) filePath = filePath + File.separator
        val dirFile = File(filePath)
        // 如果dir对应的文件不存在，或者不是一个目录，则退出
        if (!dirFile.exists() || !dirFile.isDirectory) {
            Logger.d("删除目录失败：" + filePath + "不存在！")
            return false
        }
        var flag = true
        // 删除文件夹中的所有文件包括子目录
        val files = dirFile.listFiles() ?: return false
        for (file in files) {
            // 删除子文件
            if (file.isFile) {
                flag = deleteSingleFile(file.absolutePath)
                if (!flag) break
            } else if (file.isDirectory) {
                flag = deleteDirectory(
                    file
                        .absolutePath
                )
                if (!flag) break
            }
        }
        if (!flag) {
            Logger.d("删除目录失败！")
            return false
        }
        // 删除当前目录
        return if (dirFile.delete()) {
            Logger.d("--Method--", "Copy_Delete.deleteDirectory: 删除目录" + filePath + "成功！")
            true
        } else {
            Logger.d("删除目录：" + filePath + "失败！")
            false
        }
    }

    /**
     * 读取
     * @param fileName
     * @param context
     * @return
     */
    fun readFileFromeAssets(context: Context, fileName: String?): String? {
        //将json数据变成字符串
        val stringBuilder = StringBuilder()
        try {
            //获取assets资源管理器
            val assetManager = context.assets
            //通过管理器打开文件并读取
            val bf = BufferedReader(
                InputStreamReader(
                    assetManager.open(fileName!!)
                )
            )
            var line: String?
            while (bf.readLine().also { line = it } != null) {
                stringBuilder.append(line)
            }
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return stringBuilder.toString()
    }

    /**
     * 写入文件按
     * @param content 内容
     * @param path 路径
     * @param fileName 文件名
     */
    fun writeFile(content: String?, path: String?, fileName: String) {
        var outStream: FileOutputStream? = null
        try {
            if (content == null || content.length == 0) {
                return
            }
            val parent = File(path)
            if (!parent.exists()) {
                parent.mkdirs()
            }
            val targetFile = File(parent.absolutePath + File.separator + fileName)
            if (!targetFile.exists()) {
                targetFile.createNewFile()
            }
            outStream = FileOutputStream(targetFile)
            outStream.write(content.toByteArray())
            outStream.flush()
            outStream.close()
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            if (outStream != null) {
                try {
                    outStream.close()
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }
        }
    }

    /**
     * 读取文件内容
     * @param filePath
     * @return
     */
    fun readFromeJson(filePath: String): String? {
        var content: String? = "" //文件内容字符串
        //打开文件
        val file = File(filePath)
        //如果path是传递过来的参数，可以做一个非目录的判断
        if (file.isDirectory) {
            Log.d("TestFile", "The File doesn't not exist.")
        } else {
            var instream: InputStream? = null
            try {
                instream = FileInputStream(file)
                if (instream != null) {
                    val inputreader = InputStreamReader(instream)
                    val buffreader = BufferedReader(inputreader)
                    var line: String?
                    //分行读取
                    while (buffreader.readLine().also { line = it } != null) {
                        content += line
                    }
                    instream.close()
                }
            } catch (e: FileNotFoundException) {
                Log.d("TestFile", "The File doesn't not exist.")
            } catch (e: IOException) {
                Log.d("TestFile", e.message!!)
            } finally {
                if (instream != null) {
                    try {
                        instream.close()
                    } catch (e: IOException) {
                        e.printStackTrace()
                    }
                }
            }
        }
        return content
    }

    /**
     * 压缩文件和文件夹
     *
     * @param srcFileString 要压缩的文件或文件夹
     * @param zipFileString 压缩完成的Zip路径
     * @throws Exception
     */
    @Throws(Exception::class)
    fun zipFolder(srcFileString: String?, zipFileString: String?): String? {
        val zipFile = File(zipFileString)
        val parentDir = zipFile.parent
        val parentFile = File(parentDir)
        if (!parentFile.exists()) {
            parentFile.mkdirs()
        }
        //创建ZIP
        val outZip = ZipOutputStream(FileOutputStream(zipFileString))
        //创建文件
        val file = File(srcFileString)
        //压缩
        Log.d("FileUtil", "---->" + file.parent + "===" + file.absolutePath)
        zipFiles(file.parent + File.separator, file.name, outZip)
        //完成和关闭
        outZip.finish()
        outZip.close()
        val resultFile = File(zipFileString)
        return if (resultFile.exists()) {
            zipFileString
        } else {
            null
        }
    }

    /**
     * 压缩文件
     *
     * @param folderString
     * @param fileString
     * @param zipOutputSteam
     * @throws Exception
     */
    @Throws(Exception::class)
    private fun zipFiles(
        folderString: String,
        fileString: String,
        zipOutputSteam: ZipOutputStream?,
    ) {
        Log.d(
            "FileUtil", "folderString:" + folderString + "n" +
                    "fileString:" + fileString + "n=========================="
        )
        if (zipOutputSteam == null) return
        val file = File(folderString + fileString)
        if (file.isFile) {
            val zipEntry = ZipEntry(fileString)
            val inputStream = FileInputStream(file)
            zipOutputSteam.putNextEntry(zipEntry)
            var len: Int
            val buffer = ByteArray(4096)
            while (inputStream.read(buffer).also { len = it } != -1) {
                zipOutputSteam.write(buffer, 0, len)
            }
            zipOutputSteam.closeEntry()
        } else {
            //文件夹
            val fileList = file.list()
            //没有子文件和压缩
            if (fileList == null || fileList.size <= 0) {
                val zipEntry = ZipEntry(fileString + File.separator)
                zipOutputSteam.putNextEntry(zipEntry)
                zipOutputSteam.closeEntry()
            }
            if (fileList != null) {
                //子文件和递归
                for (i in fileList.indices) {
                    zipFiles("$folderString$fileString/", fileList[i], zipOutputSteam)
                }
            }
        }
    }

    fun copyFile(oldPath: String?, newPath: String?): Boolean {
        var fileInputStream: FileInputStream? = null
        var fileOutputStream: FileOutputStream? = null
        return try {
            val oldFile = File(oldPath)
            if (!oldFile.exists()) {
                Log.e("AppUpgradeAction", "copyFile:  oldFile not exist.")
                return false
            } else if (!oldFile.isFile) {
                Log.e("AppUpgradeAction", "copyFile:  oldFile not file.")
                return false
            } else if (!oldFile.canRead()) {
                Log.e("AppUpgradeAction", "copyFile:  oldFile cannot read.")
                return false
            }

            /* 如果不需要打log，可以使用下面的语句
                 if (!oldFile.exists() || !oldFile.isFile() || !oldFile.canRead()) {
                     return false;
                 }
                 */
            val newPathParent = File(File(newPath).parent)
            if (!newPathParent.exists()) {
                newPathParent.mkdirs()
            }
            fileInputStream = FileInputStream(oldPath)
            fileOutputStream = FileOutputStream(newPath)
            val buffer = ByteArray(1024)
            var byteRead: Int
            while (-1 != fileInputStream.read(buffer).also { byteRead = it }) {
                fileOutputStream.write(buffer, 0, byteRead)
            }
            fileInputStream.close()
            fileOutputStream.flush()
            fileOutputStream.close()
            true
        } catch (e: Exception) {
            e.printStackTrace()
            false
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close()
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close()
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }
        }
    }

    fun scanDir(dir: String?): String {
        val targetDir = File(dir)
        if (!targetDir.exists()) {
            return targetDir.absolutePath + "--不存在"
        }
        var result = ""
        if (targetDir.isDirectory) {
            val files = targetDir.listFiles()
            if (files != null && files.size > 0) {
                for (file in files) {
                    result = result + scanDir(file.absolutePath)
                }
            }
        } else {
            val path = targetDir.absolutePath
            val size = getSize(path)
            result = "$result$path--$size\n"
            Log.d("scanDir", "$path--$size")
        }
        return result
    }

    fun getSize(srcUrl: String?): String {
        var fileSizeString = ""
        if (exists(srcUrl)) {
            val file = File(srcUrl)
            val len = file.length()
            val df = DecimalFormat("#.00")
            fileSizeString = if (len < 1024) {
                df.format(len.toDouble()) + "B"
            } else if (len < 1048576) {
                df.format(len.toDouble() / 1024) + "K"
            } else if (len < 1073741824) {
                df.format(len.toDouble() / 1048576) + "M"
            } else {
                df.format(len.toDouble() / 1073741824) + "G"
            }
        }
        return fileSizeString
    }

    fun size(size: Long): String? {
        return if (size / (1024 * 1024 * 1024) > 0) {
            val tmpSize = size.toFloat() / (1024 * 1024 * 1024).toFloat()
            val df = DecimalFormat("#.##")
            "" + df.format(tmpSize.toDouble()) + "GB"
        } else if (size / (1024 * 1024) > 0) {
            val tmpSize = size.toFloat() / (1024 * 1024).toFloat()
            val df = DecimalFormat("#.##")
            "" + df.format(tmpSize.toDouble()) + "MB"
        } else if (size / 1024 > 0) {
            "" + size / 1024 + "KB"
        } else "" + size + "B"
    }

    fun exists(srcUrl: String?): Boolean {
        val file = File(srcUrl)
        return file.exists() && file.isFile
    }

    /**
     * 删除多余的安装包
     * @param context
     * @param path
     */
    fun deleteInstallPackageFile(context: Context, path: String?) {
        try {
            if (TextUtils.isEmpty(path)) {
                return
            }
            val file = File(path)
            if (!file.exists() || file.isFile) {
                return
            }
            val assetsAppInfo: String? = readFileFromeAssets(
                context,
                "innerAppInfo.json"
            )
            val assetsAppJson = JSONObject(assetsAppInfo)
            val assisName = assetsAppJson.getString("assisAppFileName")
            val uniappName = assetsAppJson.getString("uniAppFileName")
            val deletePwdName = assetsAppJson.getString("deletePasswordAppFileName")
            val lockFirmwareName = assetsAppJson.getString("lockPackage")
            val cameraFirmwareName = assetsAppJson.getString("cameraFirmwareFileName")
            val files = file.listFiles()
            if (files != null && files.size > 0) {
                for (f in files) {
                    if (f.isFile) {
                        val fileName = f.name
                        if (fileName.contains("Assis") && fileName != assisName || fileName.contains(
                                "UniApp"
                            ) && fileName != uniappName || fileName.contains("delete-passwd") && fileName != deletePwdName || fileName.contains(
                                "AImodeIO"
                            ) && fileName != lockFirmwareName || fileName.endsWith(".pak") && fileName != cameraFirmwareName
                        ) {
                            if (f.delete()) {
                                Log.d("deleteInstallPackage", f.absolutePath)
                            }
                        }
                    }
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
            Logger.d("删除多余安装包异常：" + e.message)
            return
        }
    }

    /**
     * 准备日志文件
     * @param date 日期 yyyyMMdd
     * @return
     */
    fun prepareLogFile(logPathStr: String, date: String, zipName: String = DateTimeUtil.nowDateTimeSp): String? {
        return try {
            val count = 0

            //日志
            val logDir = "$logPathStr/$date"
            val logFileDir = File(logDir)
            if (!logFileDir.exists()) {
                return null
            }

            zipFolder(
                logFileDir.absolutePath,
                logPathStr + "/temp/" + zipName + ".zip"
            )
            //压缩完成后删除复制过来的日志文件
            //            FileUtil.delete(Constants.LOG_ZIP_PATH + date);
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

    fun prepareLogFiles(logPathStr: String, dates: MutableList<String>, zipName: String = DateTimeUtil.nowDateTimeSp): String?{
        return try {
            val count = 0

            val zipFilePath = mutableListOf<String>()
            val singleFile = dates.size < 2
            val timesp = DateTimeUtil.nowDateTimeSp
            val logTempPathStr = logPathStr + "/temp/" + timesp + "/" + zipName

            dates.forEach { date ->
                //日志
                val logDir = "$logPathStr/$date"
                val logFileDir = File(logDir)
                if (!logFileDir.exists()) {
                    return@forEach
                }

                val path = if (singleFile){
                    logTempPathStr + ".zip"
                }else{
                    logTempPathStr+ "/" + date + ".zip"
                }

                zipFolder(
                    logFileDir.absolutePath,
                    path
                )?.let { zipPath ->
                    zipFilePath.add(zipPath)
                }
            }

            when (zipFilePath.size){
                0 -> null
                1 -> zipFilePath[0]
                else -> {
                    val zipFileName = "${dates.size}_days"
                    val zipFinalPath = logTempPathStr.split("/").run {
                        logTempPathStr.replace(this[size - 1], zipName)
                    }
                    val result = zipFolder(
                        logTempPathStr,
                        zipFinalPath + ".zip"
                    )
                    result
                }
            }
            //压缩完成后删除复制过来的日志文件
            //            FileUtil.delete(Constants.LOG_ZIP_PATH + date);
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

    /**
     * 查找媒体资源
     * @param mediaPath 路径
     * @return listItem="path,type" 1-图片 2-视频
     */
    fun getMediaFile(mediaPath: String): MutableList<String>? {
        return try {
            val result: MutableList<String> = mutableListOf()
            val sourceDir = File(mediaPath)
            if (sourceDir.exists() && sourceDir.isDirectory) {
                sourceDir.listFiles()?.run {
                    forEach { file ->
                        when (isMediaFile(file)){
                            0 -> {}
                            1 -> {result.add("${file.absolutePath},1")}
                            2 -> {result.add("${file.absolutePath},2")}
                            3 -> {}
                        }
                    }
                }
            }
            result
        } catch (e: Exception) {
            e.printStackTrace()
            logFile("获取本地广告资源异常：${e.message}")
            null
        }
    }

    private fun isMediaFile(file: File): Int {
        return try {
            if (file.isFile) {
                if (file.name.endsWith(".mp4") || file.name.endsWith(".mkv")){
                    return 2
                }else if(file.name.endsWith(".gif") || file.name.endsWith(".jpg") || file.name.endsWith(".png") || file.name.endsWith(".webp") || file.name.endsWith(".jpeg")) {
                    return 1
                }else{
                    return 3
                }
            }
            0
        } catch (e: Exception) {
            e.printStackTrace()
            0
        }
    }

    fun saveBytes(byteArray: ByteArray, savePath: String, fileName: String){

    }

    /**
     * 合并文件
     * @param outPutFile 输出文件
     * @param files 合并文件路径
     */
    fun mergeFiles(outPutFile: String, files: MutableList<String>): Boolean{
        val fileSum = files.size
        if (fileSum == 0){
            return false
        }

        var result = false
        var raf: RandomAccessFile? = null
        try {
            raf = RandomAccessFile(File(outPutFile), "rw")
            for (i in files.indices){
                val file = File(files[i])
                val reader = RandomAccessFile(file, "r")
                val b = ByteArray(1024)
                var len = 0
                do {
                    len = reader.read(b)
                    if (len != -1){
                        raf.write(b, 0, len);//写
                    }
                }while (len != -1)
            }
            result = true
        }catch (e: Exception){
            e.printStackTrace()
        }finally {
            try {
                raf?.close()
            }catch (e: Exception){
                e.printStackTrace()
            }

        }
        return result
    }

}